/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.ext.sql;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import org.apache.commons.compress.utils.Lists;
import org.elsfs.cloud.common.core.entity.BaseEntity;
import org.elsfs.cloud.common.mybatis.ext.sql.support.builder.StandardSelectSql;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.FieldEnumResolverSupport;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.HierarchyTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.SimpleTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.from.FromItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.from.StandardFromItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.from.SubQueryFromItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.CompositeSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.ExpressionSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.StandardSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SubQuerySelectItem;
import org.elsfs.cloud.common.util.lang.ArrayUtils;
import org.elsfs.cloud.common.util.lang.CollectionUtils;
import org.elsfs.cloud.common.util.lang.StringUtils;

/**
 * 抽象查询SQL实现
 *
 * @param <C> 子类具体类型
 */
@SuppressWarnings("unchecked")
public abstract class AbstractSelectSql<C extends AbstractSelectSql<C>>
    extends AbstractSelectJoin<C>
    implements SelectSql<
        C,
        AbstractSelectSql<C>.SelectConditionBuilderImpl,
        AbstractSelectCondition<AbstractSelectSql<C>.SelectConditionBuilderImpl>
            .HavingConditionBuilderImpl> {

  /** 子类实现 */
  protected final C childThis = (C) this;

  /** 选择项列表 */
  private final List<SelectItem> selectItems = Lists.newArrayList();

  /** From项 */
  private FromItem fromItem;

  /** 查询条件构建器 */
  private SelectConditionBuilderImpl selectConditionBuilder;

  /**
   * 解析字段枚举名称
   *
   * @param field 字段枚举
   * @return 字段名称
   */
  protected String resolveFieldName(Enum<?> field) {
    return FieldEnumResolverSupport.resolveFieldName(this.getTableAliasManager(), field);
  }

  /**
   * 选择SQL字段
   *
   * @param fields SQL字段
   * @return 具体实现
   */
  @Override
  public C select(String... fields) {
    if (ArrayUtils.isEmpty(fields)) {
      return this.childThis;
    }

    for (String field : fields) {
      this.selectItems.add(new StandardSelectItem(field));
    }

    return this.childThis;
  }

  /**
   * 选择SQL字段
   *
   * @param fields SQL字段枚举
   * @return 具体实现
   */
  @Override
  public C select(Enum<?>... fields) {
    if (ArrayUtils.isEmpty(fields)) {
      return this.childThis;
    }

    for (Enum<?> field : fields) {
      this.selectItems.add(new StandardSelectItem(this.resolveFieldName(field)));
    }

    return this.childThis;
  }

  /**
   * 选择SQL字段
   *
   * @param fields SQL字段
   * @return 具体实现
   */
  @Override
  public C select(Collection<String> fields) {
    if (CollectionUtils.isEmpty(fields)) {
      return this.childThis;
    }

    for (String field : fields) {
      this.selectItems.add(new StandardSelectItem(field));
    }

    return this.childThis;
  }

  /**
   * 选择SQL字段表达式
   *
   * @param expressions SQL字段表达式
   * @return 具体实现
   */
  @Override
  public C selectExpression(String... expressions) {
    if (ArrayUtils.isEmpty(expressions)) {
      return this.childThis;
    }

    for (String expression : expressions) {
      this.selectItems.add(new ExpressionSelectItem(expression));
    }

    return this.childThis;
  }

  /**
   * 选择SQL字段表达式
   *
   * @param expressions SQL字段表达式
   * @return 具体实现
   */
  @Override
  public C selectExpression(Collection<String> expressions) {
    if (CollectionUtils.isEmpty(expressions)) {
      return this.childThis;
    }

    for (String expression : expressions) {
      this.addSelectItem(new ExpressionSelectItem(expression));
    }

    return this.childThis;
  }

  /**
   * 选择SQL字段
   *
   * @param field SQL字段
   * @param fieldAlias 字段别名
   * @return 具体实现
   */
  @Override
  public C selectAs(String field, String fieldAlias) {
    if (StringUtils.isBlank(fieldAlias)) {
      throw new MybatisPlusException("选择字段[" + field + "]的别名不能为空");
    }

    this.addSelectItem(new StandardSelectItem(field + " AS " + fieldAlias));
    return this.childThis;
  }

  /**
   * 选择SQL字段
   *
   * @param field SQL字段枚举
   * @param fieldAlias 字段别名
   * @return 具体实现
   */
  @Override
  public C selectAs(Enum<?> field, String fieldAlias) {
    return this.selectAs(this.resolveFieldName(field), fieldAlias);
  }

  /**
   * 选择SQL字段
   *
   * @param field SQL字段枚举
   * @param fieldAlias 字段别名枚举
   * @return 具体实现
   */
  @Override
  public C selectAs(Enum<?> field, Enum<?> fieldAlias) {
    return this.selectAs(field, fieldAlias.name());
  }

  /**
   * 选择子查询结果
   *
   * @param sqlBuilderConsumer SQL构建器消费器
   * @param fieldAlias 字段别名
   * @return 具体实现
   */
  @Override
  public C selectAs(Consumer<StandardSelectSql> sqlBuilderConsumer, String fieldAlias) {
    StandardSelectSql standardSelectSql =
        new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(), this.getTableAliasManager()));
    sqlBuilderConsumer.accept(standardSelectSql);
    this.addSelectItem(new SubQuerySelectItem(standardSelectSql.buildSqlFragment(), fieldAlias));

    return this.childThis;
  }

  /**
   * 选择子查询结果
   *
   * @param sqlBuilderConsumer SQL构建器消费器
   * @param fieldAlias 字段别名枚举
   * @return 具体实现
   */
  @Override
  public C selectAs(Consumer<StandardSelectSql> sqlBuilderConsumer, Enum<?> fieldAlias) {
    return this.selectAs(sqlBuilderConsumer, fieldAlias.name());
  }

  /**
   * 选择SQL字段
   *
   * @param fields SQL字段枚举
   * @return 具体实现
   */
  @Override
  public C selectEnum(Collection<Enum<?>> fields) {
    if (CollectionUtils.isEmpty(fields)) {
      return this.childThis;
    }

    for (Enum<?> field : fields) {
      this.addSelectItem(new StandardSelectItem(this.resolveFieldName(field)));
    }

    return this.childThis;
  }

  /**
   * 添加选择项
   *
   * @param selectItem 选择项
   */
  @Override
  protected void addSelectItem(SelectItem selectItem) {
    this.selectItems.add(selectItem);
  }

  /**
   * 设置查询表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 具体实现
   */
  @Override
  public C from(Class<? extends BaseEntity> entityClass, String tableAlias) {
    this.fromItem =
        new StandardFromItem(TableInfoHelper.getTableInfo(entityClass).getTableName(), tableAlias);

    this.getTableAliasManager().registerAlias(entityClass, tableAlias);
    return this.childThis;
  }

  /**
   * 设置查询表
   *
   * @param table 表名
   * @param tableAlias 表别名
   * @return 具体实现
   */
  @Override
  public C from(String table, String tableAlias) {
    this.fromItem = new StandardFromItem(table, tableAlias);

    this.getTableAliasManager().registerAlias(table, tableAlias);
    return this.childThis;
  }

  /**
   * 设置查询表
   *
   * @param entityClass 实体类
   * @return 具体实现
   */
  @Override
  public C from(Class<? extends BaseEntity> entityClass) {
    this.fromItem =
        new StandardFromItem(TableInfoHelper.getTableInfo(entityClass).getTableName(), TABLE_ALIAS);

    this.getTableAliasManager().registerAlias(entityClass, TABLE_ALIAS);
    return this.childThis;
  }

  /**
   * 设置查询表
   *
   * @param table 表名
   * @return 具体实现
   */
  @Override
  public C from(String table) {
    this.fromItem = new StandardFromItem(table, TABLE_ALIAS);

    this.getTableAliasManager().registerAlias(table, TABLE_ALIAS);
    return this.childThis;
  }

  /**
   * 设置子查询表
   *
   * @param sqlBuilderConsumer SQL构建器消费器
   * @param tableAlias 表别名
   * @return 具体实现
   */
  @Override
  public C from(Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    StandardSelectSql standardSelectSql =
        new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(), this.getTableAliasManager()));
    sqlBuilderConsumer.accept(standardSelectSql);
    this.fromItem = new SubQueryFromItem(standardSelectSql.buildSqlFragment(), tableAlias);

    return this.childThis;
  }

  /**
   * 设置Where条件
   *
   * @return 条件构建器
   */
  @Override
  public SelectConditionBuilder<
          SelectConditionBuilderImpl,
          AbstractSelectCondition<SelectConditionBuilderImpl>.HavingConditionBuilderImpl,
          C>
      where() {
    if (this.selectConditionBuilder == null) {
      this.selectConditionBuilder = new SelectConditionBuilderImpl();
    }

    return this.selectConditionBuilder;
  }

  /** 构建SQL之前处理 */
  @Override
  protected void beforeBuild() {
    if (this.selectItems.isEmpty()) {
      this.selectItems.add(new CompositeSelectItem(List.of(new StandardSelectItem("*"))));
    } else {
      this.addSqlFragment(
          new CompositeSelectItem(
              this.selectItems.stream()
                  .map(
                      selectItem -> {
                        if (selectItem instanceof StandardSelectItem standardSelectItem
                            && StringUtils.isBlank(standardSelectItem.getTableAlias())) {
                          return new StandardSelectItem(
                              this.fromItem.getTableAlias(),
                              standardSelectItem.getField(),
                              standardSelectItem.getFieldAlias());
                        }

                        return selectItem;
                      })
                  .toList()));
    }

    if (this.fromItem == null) {
      throw new MybatisPlusException("SQL构建未指定【FROM】");
    }

    this.addSqlFragment(this.fromItem);
    super.beforeBuild();

    if (this.selectConditionBuilder != null) {
      this.addSqlFragment(this.selectConditionBuilder);
    }
  }

  /** 查询条件构建器实现 */
  public class SelectConditionBuilderImpl
      extends AbstractSelectCondition<SelectConditionBuilderImpl>
      implements SelectConditionBuilder<
          SelectConditionBuilderImpl,
          AbstractSelectCondition<SelectConditionBuilderImpl>.HavingConditionBuilderImpl,
          C> {

    /**
     * 获取SQL参数管理器
     *
     * @return SQL参数管理器
     */
    @Override
    public SqlParameterManager getSqlParameterManager() {
      return AbstractSelectSql.this.getSqlParameterManager();
    }

    /**
     * 返回上一级对象
     *
     * @return 上级对象
     */
    @Override
    public C end() {
      return AbstractSelectSql.this.childThis;
    }

    /**
     * 获取表别名管理器
     *
     * @return 表名别管理器
     */
    @Override
    public TableAliasManager getTableAliasManager() {
      return AbstractSelectSql.this.getTableAliasManager();
    }
  }
}
